using System;
using System.Collections.Generic;
using System.Xml.Linq;
using TTEngine;
using TTEngine.Resource;
using Silk.NET.OpenGLES;

namespace TTEngine.Graphics
{
    public class ShaderProgram : ResourceObj, IResource
    {

        private uint _program;
        private ShaderObj _vs;
        private ShaderObj _fs;
        public readonly Dictionary<string, UniformInfo> _uniformInfos = new Dictionary<string, UniformInfo>();
        public readonly Dictionary<string, LocationInfo> _attribInfos = new Dictionary<string, LocationInfo>();
        private bool _disposed;

        public uint ProgID
        {
            get
            {
                return _program;
            }
        }

        public ShaderProgram(ResourcePackage package, ShaderObj vs, ShaderObj fs)
            : base(package)
        {
            _program = TTEngine.Runtime.activeGL.CreateProgram();
            if (_program == 0)
            {
                throw new Exception("LinkShader error gl.CreateProgram");
            }
            Link(vs, fs);
        }
        protected override void OnDestory()
        {
            if (!_disposed)
            {
                TTEngine.Runtime.activeGL.DeleteProgram(_program);
                if (_vs.UseCount > 0)
                    _vs.DecRef();
                if (_fs.UseCount > 0)
                    _fs.DecRef();
                _disposed = true;
            }
        }
        void Link(ShaderObj vs, ShaderObj fs)
        {
            _vs = vs;

            _fs = fs;
            _vs.AddRef();
            _fs.AddRef();

            TTEngine.Runtime.activeGL.AttachShader(_program, _vs.Handle);
            TTEngine.Runtime.activeGL.AttachShader(_program, _fs.Handle);
            TTEngine.Runtime.activeGL.LinkProgram(_program);
            var state = TTEngine.Runtime.activeGL.GetProgram(_program, ProgramPropertyARB.LinkStatus) > 0;
            if (!state)
            {
                string infoLog = TTEngine.Runtime.activeGL.GetProgramInfoLog(_program);
                throw new Exception($"LinkShader error gl.LinkProgram: {UniqueName} E: {infoLog}");
            }


            // 获取并存储属性位置
            int activeAttrs = TTEngine.Runtime.activeGL.GetProgram(_program, ProgramPropertyARB.ActiveAttributes);
            for (uint i = 0; i < activeAttrs; i++)
            {
                var attr = TTEngine.Runtime.activeGL.GetActiveAttrib(_program, i, out var size, out var type);
                if (attr != null)
                {
                    int loc = TTEngine.Runtime.activeGL.GetAttribLocation(_program, attr);
                    _attribInfos[attr] = new LocationInfo
                    {
                        Location = loc,
                        Size = size,
                        Type = type
                    };
                }
            }

            // 获取并存储统一变量位置
            int activeUniforms = TTEngine.Runtime.activeGL.GetProgram(_program, ProgramPropertyARB.ActiveUniforms);
            for (uint i = 0; i < activeUniforms; i++)
            {
                var info = TTEngine.Runtime.activeGL.GetActiveUniform(_program, i, out var size, out var type);
                if (info != null)
                {
                    int loc = TTEngine.Runtime.activeGL.GetUniformLocation(_program, info);
                    if (loc != -1)
                    {

                        _uniformInfos[info] = new UniformInfo
                        {
                            isBlock = false,
                            Type = type,
                            Location = loc,
                            Size = size,

                        };
                    }
                }
            }
            var useblock = TTEngine.Runtime.activeGL.GetProgram(_program, ProgramPropertyARB.ActiveUniformBlocks);
            for (uint i = 0; i < useblock; i++)
            {
                TTEngine.Runtime.activeGL.GetActiveUniformBlockName(_program, i, 255u, out var keylen, out string key);
                var locindex = (int)TTEngine.Runtime.activeGL.GetUniformBlockIndex(_program, key);
                if (locindex < 0) continue;

                _uniformInfos[key] = new UniformInfo
                {
                    isBlock = true,
                    //Type =  UniformType.,
                    Location = locindex,
                    Size = 0,
                };
            }
        }

        public void Use()
        {
            TTEngine.Runtime.activeGL.UseProgram(_program);
        }


    }
}